package main

import (
	"time"
)

// RWMutex 实现

type _RWMutex struct {
	readerCount int32
	writerCount int32
	readerSem   chan struct{}
	writerSem   chan struct{}
}

// _newRWMutex 新建一个RWMutex锁
func _newRWMutex() *_RWMutex {
	return &_RWMutex{
		readerSem: make(chan struct{}, 1),
		writerSem: make(chan struct{}, 1),
	}
}

// _rLock 获取读锁
func (rw *_RWMutex) _rLock() {
	for {
		// 没有写锁
		if rw.writerCount == 0 {
			rw.readerCount++
			return
		}
		// 阻塞写锁等待释放
		rw.readerSem <- struct{}{}
		<-rw.readerSem
	}
}

// _rUnlock 释放读锁
func (rw *_RWMutex) _rUnlock() {
	rw.readerCount--
	if rw.readerCount == 0 {
		// 单一信号 _rUnlock 只需要发送一个信号来唤醒一个等待的写操作。这可以通过向通道发送一个值来实现，而不需要关闭通道
		// 保持通道可用 保持 writerSem 通道开放允许它在多次读写操作之间重复使用，而不需要频繁地关闭和重新创建
		// 避免复杂性 简单地发送一个值比关闭和重新创建通道更高效，也更不容易出错
		rw.writerSem <- struct{}{}
	}
}

// _lock 获取写锁
func (rw *_RWMutex) _lock() {
	for {
		if rw.readerCount == 0 {
			rw.writerCount++
			return
		}
		<-rw.writerSem
	}
}

// _unlock 释放写锁
func (rw *_RWMutex) _unlock() {
	rw.writerCount--
	if rw.writerCount == 0 {
		// 1.唤醒所有等待的读操作：通过关闭 writerSem 通道，
		//所有在 _rLock 函数中等待的读操作都会被唤醒。
		//这是因为从一个已关闭的通道接收数据会立即返回零值，而不会阻塞
		// 2.重置通道状态：关闭通道后，通过 make 函数重新创建一个新的通道，确保下一次写锁操作时有一个干净的通道可用
		// 3.避免死锁：如果不关闭并重新创建通道，可能会导致一些 goroutine 永远阻塞在旧的通道上
		close(rw.writerSem)
		rw.writerSem = make(chan struct{}, 1)
	}
}

type _data struct {
	mu    *_RWMutex
	value int
}

func (d *_data) Read() int {
	d.mu._rLock()
	defer d.mu._rUnlock()

	time.Sleep(100 * time.Microsecond)

	return d.value
}

func (d *_data) Write(value int) {
	d.mu._lock()
	defer d.mu._unlock()

	// 模拟一些处理
	time.Sleep(100 * time.Millisecond)

	d.value = value
}

//func main() {
//	data := &_data{mu: _newRWMutex()}
//
//	for i := 0; i < 5; i++ {
//		go func(id int) {
//			val := data.Read()
//			fmt.Printf("Reader %d read value: %d\n", id, val)
//		}(i)
//	}
//
//	go func() {
//		data.Write(32)
//		fmt.Println("write wrote value : 32")
//	}()
//
//	time.Sleep(1 * time.Second)
//}
